using System;
using System.Collections.Generic;
using System.Text;
using Csla;
using Templates;
using NBearLite;
using Active.Core.DB;
using Csla.Data;

namespace Active.Core
{
    [Serializable()]
    public class Agent : BusinessBase<Agent>
    {
        #region Business Methods
        // TODO: add your own fields, properties and methods
        private int _id;
        public int Id
        {
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            get
            {
                CanReadProperty(true);
                return _id;
            }
            //[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            //set
            //{
            //    CanWriteProperty(true);
            //    if (_id != value)
            //    {
            //        _id = value;
            //        PropertyHasChanged();
            //    }
            //}
        }

        string  _name = string.Empty;
        /// <summary>
        /// Gets or sets the agent's company name.
        /// </summary>
        /// <value>The name.</value>
        public string  Name
        {
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            get
            {
                CanReadProperty(true);
                return _name;
            }
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            set
            {
                CanWriteProperty(true);
                if (value == null) value = string.Empty;
                if (!_name.Equals(value))
                {
                    _name = value;
                    PropertyHasChanged();
                }
            }
        }

        string _tel = string.Empty;
        /// <summary>
        /// Gets or sets the tel.
        /// </summary>
        /// <value>The tel.</value>
        public string Tel
        {
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            get
            {
                CanReadProperty(true);
                return _tel;
            }
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            set
            {
                CanWriteProperty(true);
                if (value == null) value = string.Empty;
                if (!_tel.Equals(value))
                {
                    _tel = value;
                    PropertyHasChanged();
                }
            }
        }

        string _fax = string.Empty;
        /// <summary>
        /// Gets or sets the fax.
        /// </summary>
        /// <value>The fax.</value>
        public string Fax
        {
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            get
            {
                CanReadProperty(true);
                return _fax;
            }
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            set
            {
                CanWriteProperty(true);
                if (value == null) value = string.Empty;
                if (!_fax.Equals(value))
                {
                    _fax = value;
                    PropertyHasChanged();
                }
            }
        }

        string _mobile = string.Empty;
        /// <summary>
        /// Gets or sets the mobile.
        /// </summary>
        /// <value>The mobile.</value>
        public string  Mobile
        {
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            get
            {
                CanReadProperty(true);
                return _mobile;
            }
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            set
            {
                CanWriteProperty(true);
                if (value == null) value = string.Empty;
                if (!_mobile.Equals(value))
                {
                    _mobile = value;
                    PropertyHasChanged();
                }
            }
        }

        string _email = string.Empty;
        /// <summary>
        /// Gets or sets the email.
        /// </summary>
        /// <value>The email.</value>
        public string Email
        {
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            get
            {
                CanReadProperty(true);
                return _email;
            }
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            set
            {
                CanWriteProperty(true);
                if (value == null) value = string.Empty;
                if (!_email.Equals(value))
                {
                    _email = value;
                    PropertyHasChanged();
                }
            }
        }

        string _msn = string.Empty;
        /// <summary>
        /// Gets or sets the MSN.
        /// </summary>
        /// <value>The MSN.</value>
        public string MSN
        {
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            get
            {
                CanReadProperty(true);
                return _msn;
            }
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            set
            {
                CanWriteProperty(true);
                if (value == null) value = string.Empty;
                if (!_msn.Equals(value))
                {
                    _msn = value;
                    PropertyHasChanged();
                }
            }
        }

        string _address = string.Empty;
        /// <summary>
        /// Gets or sets the address.
        /// </summary>
        /// <value>The address.</value>
        public string Address
        {
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            get
            {
                CanReadProperty(true);
                return _address;
            }
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            set
            {
                CanWriteProperty(true);
                if (value == null) value = string.Empty;
                if (!_address.Equals(value))
                {
                    _address = value;
                    PropertyHasChanged();
                }
            }
        }

        string _postCode = string.Empty;
        /// <summary>
        /// Gets or sets the post code.
        /// </summary>
        /// <value>The post code.</value>
        public string PostCode
        {
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            get
            {
                CanReadProperty(true);
                return _postCode;
            }
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            set
            {
                CanWriteProperty(true);
                if (value == null) value = string.Empty;
                if (!_postCode.Equals(value))
                {
                    _postCode = value;
                    PropertyHasChanged();
                }
            }
        }

        int _dealTimes;
        /// <summary>
        /// Gets or sets the deal times.
        /// </summary>
        /// <value>The deal times.</value>
        public int DealTimes
        {
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            get
            {
                CanReadProperty(true);
                return _dealTimes;
            }
        }

        Contacts _contactList;
        /// <summary>
        /// Gets the contact list.
        /// </summary>
        /// <value>The contact list.</value>
        public Contacts ContactList
        {
            get
            {
                if (_contactList == null && _id > 0)
                {
                    _contactList = Contacts.GetAgentContacts(_id);
                }
                return _contactList;
            }
        }

        /// <summary>
        /// Override this method to return a unique identifying
        /// value for this object.
        /// </summary>
        /// <returns></returns>
        /// <remarks>
        /// If you can not provide a unique identifying value, it
        /// is best if you can generate such a unique value (even
        /// temporarily). If you can not do that, then return
        /// <see langword="Nothing"/> and then manually override the
        /// <see cref="Equals"/>, <see cref="GetHashCode"/> and
        /// <see cref="ToString"/> methods in your business object.
        /// </remarks>
        protected override object GetIdValue()
        {
            return _id;
        }

        #endregion

        #region Validation Rules

        /// <summary>
        /// Override this method in your business class to
        /// be notified when you need to set up shared
        /// business rules.
        /// </summary>
        /// <remarks>
        /// This method is automatically called by CSLA .NET
        /// when your object should associate per-type
        /// validation rules with its properties.
        /// </remarks>
        protected override void AddBusinessRules()
        {
            // TODO: add validation rules
            //ValidationRules.AddRule(null, "");
            ValidationRules.AddRule(Csla.Validation.CommonRules.StringRequired, "Name");
        }

        #endregion

        #region Authorization Rules

        /// <summary>
        /// Override this method to add per-type
        /// authorization rules for your type's properties.
        /// </summary>
        /// <remarks>
        /// AddAuthorizationRules is automatically called by CSLA .NET
        /// when your object should associate per-type authorization roles
        /// with its properties.
        /// </remarks>
        protected override void AddAuthorizationRules()
        {
            // TODO: add authorization rules
            //AuthorizationRules.AllowWrite("", "");
        }

        /// <summary>
        /// Determines whether this instance [can add object].
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if this instance [can add object]; otherwise, <c>false</c>.
        /// </returns>
        public static bool CanAddObject()
        {
            return Utils.IsCustomerEmployee
                || Utils.IsCustomerManager;
        }

        /// <summary>
        /// Determines whether this instance [can get object].
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if this instance [can get object]; otherwise, <c>false</c>.
        /// </returns>
        public static bool CanGetObject()
        {
            return true;
        }

        /// <summary>
        /// Determines whether this instance [can edit object].
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if this instance [can edit object]; otherwise, <c>false</c>.
        /// </returns>
        public static bool CanEditObject()
        {
            return Utils.IsCustomerEmployee
                || Utils.IsCustomerManager;
        }

        /// <summary>
        /// Determines whether this instance [can delete object].
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if this instance [can delete object]; otherwise, <c>false</c>.
        /// </returns>
        public static bool CanDeleteObject()
        {
            return Utils.IsCustomerManager;
        }

        #endregion

        #region Factory Methods

        /// <summary>
        /// News the agent.
        /// </summary>
        /// <returns></returns>
        public static Agent NewAgent()
        {
            if (!CanAddObject())
                throw new System.Security.SecurityException(
                  "User not authorized to add an agent");
            return DataPortal.Create<Agent>();
        }

        /// <summary>
        /// Gets the agent.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <returns></returns>
        public static Agent GetAgent(int id)
        {
            if (!CanGetObject())
                throw new System.Security.SecurityException(
                  "User not authorized to view an agent");
            return DataPortal.Fetch<Agent>(new Criteria(id));
        }

        /// <summary>
        /// Gets the agent.
        /// </summary>
        /// <param name="agentName">Name of the agent.</param>
        /// <returns></returns>
        public static Agent GetAgent(string agentName)
        {
            if (!CanGetObject())
                throw new System.Security.SecurityException(
                  "User not authorized to view an agent");
            return DataPortal.Fetch<Agent>(new Criteria(agentName));
        }

        /// <summary>
        /// Deletes the agent.
        /// </summary>
        /// <param name="id">The id.</param>
        public static void DeleteAgent(int id)
        {
            if (!CanDeleteObject())
                throw new System.Security.SecurityException(
                  "User not authorized to remove an agent");
            DataPortal.Delete(new Criteria(id));
        }

        /// <summary>
        /// Updates the deal times.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <returns></returns>
        internal static int UpdateDealTimes(int agentId)
        {
            UpdateDealTimesCommand result = DataPortal.Execute<UpdateDealTimesCommand>(new UpdateDealTimesCommand(agentId));
            return result.DealTimes;
        }
        
        /// <summary>
        /// Saves the object to the database.
        /// </summary>
        /// <returns>
        /// A new object containing the saved values.
        /// </returns>
        /// <remarks>
        /// 	<para>
        /// Calling this method starts the save operation, causing the object
        /// to be inserted, updated or deleted within the database based on the
        /// object's current state.
        /// </para><para>
        /// If <see cref="Core.BusinessBase.IsDeleted"/> is <see langword="true"/>
        /// the object will be deleted. Otherwise, if <see cref="Core.BusinessBase.IsNew"/>
        /// is <see langword="true"/> the object will be inserted.
        /// Otherwise the object's data will be updated in the database.
        /// </para><para>
        /// All this is contingent on <see cref="Core.BusinessBase.IsDirty"/>. If
        /// this value is <see langword="false"/>, no data operation occurs.
        /// It is also contingent on <see cref="Core.BusinessBase.IsValid"/>.
        /// If this value is <see langword="false"/> an
        /// exception will be thrown to indicate that the UI attempted to save an
        /// invalid object.
        /// </para><para>
        /// It is important to note that this method returns a new version of the
        /// business object that contains any data updated during the save operation.
        /// You MUST update all object references to use this new version of the
        /// business object in order to have access to the correct object data.
        /// </para><para>
        /// You can override this method to add your own custom behaviors to the save
        /// operation. For instance, you may add some security checks to make sure
        /// the user can save the object. If all security checks pass, you would then
        /// invoke the base Save method via <c>base.Save()</c>.
        /// </para>
        /// </remarks>
        public override Agent Save()
        {
            if (IsDeleted && !CanDeleteObject())
                throw new System.Security.SecurityException(
                  "User not authorized to remove an agent");
            else if (IsNew && !CanAddObject())
                throw new System.Security.SecurityException(
                  "User not authorized to add an agent");
            else if (!CanEditObject())
                throw new System.Security.SecurityException(
                  "User not authorized to update an agent");
            return base.Save();
        }

        private Agent()
        { /* Require use of factory methods */ }

        #endregion

        #region UpdateDealTimesCommand

        [Serializable()]
        private class UpdateDealTimesCommand : CommandBase
        {
            private int _agentId;
            private int _dealTimes;
            public UpdateDealTimesCommand(int agentId)
            {
                _agentId = _agentId;
            }

            public int DealTimes
            {
                get { return _dealTimes; }
            }

            [Transactional(TransactionalTypes.TransactionScope)]
            protected override void DataPortal_Execute()
            {
                int count =(int)
                    DataService.DBService.Select(Tables.Bills, Tables.Bills.Id.Count())
                    .Where(Tables.Bills.AgentId == _agentId).ToScalar();
                count++;
                DataService.DBService.Update(Tables.Agents)
                    .AddColumn(Tables.Agents.DealTimes, count)
                    .Where(Tables.Agents.Id == _agentId).Execute();
                _dealTimes = count;
            }
        }

        #endregion

        #region Data Access

        [Serializable()]
        private class Criteria
        {
            private int _id;
            public int Id
            {
                get { return _id; }
            }

            private string _name;
            public string Name
            {
                get { return _name; }
            }
            public Criteria(int id)
            { _id = id; }

            public Criteria(string name)
            {
                _name = name;
            }
        }

        /// <summary>
        /// Override this method to load a new business object with default
        /// values from the database.
        /// </summary>
        /// <remarks>
        /// Normally you will overload this method to accept a strongly-typed
        /// criteria parameter, rather than overriding the method with a
        /// loosely-typed criteria parameter.
        /// </remarks>
        [RunLocal]
        protected override void DataPortal_Create()
        {
            //nothing to initialize
        }

        private void DataPortal_Fetch(Criteria criteria)
        {
            SelectSqlSection select =
                DataService.DBService.Select(Tables.Agents);
            NBearLite.WhereClip where = WhereClip.All;
            if (criteria.Id > 0)
            {
                where.And(Tables.Agents.Id == criteria.Id);
            }
            else
            {
                where.And(Tables.Agents.Name == criteria.Name);
            }
            select.Where(where);
            using (SafeDataReader dr = new SafeDataReader(select.ToDataReader()))
            {
                dr.Read();
                this._address = dr.GetString("Address");
                this._dealTimes = dr.GetInt32("DealTimes");
                this._email = dr.GetString("Email");
                this._fax = dr.GetString("Fax");
                this._id = dr.GetInt32("Id");
                this._mobile = dr.GetString("Mobile");
                this._msn = dr.GetString("MSN");
                this._name = dr.GetString("Name");
                this._postCode = dr.GetString("PostCode");
                this._tel = dr.GetString("Tel");
            }
        }

        [Transactional(TransactionalTypes.TransactionScope)]
        protected override void DataPortal_Insert()
        {
            InsertSqlSection insert =
                DataService.DBService.Insert(Tables.Agents)
                .AddColumn(Tables.Agents.Address, this._address)
                .AddColumn(Tables.Agents.DealTimes, this._dealTimes)
                .AddColumn(Tables.Agents.Email, _email)
                .AddColumn(Tables.Agents.Fax, _fax)
                .AddColumn(Tables.Agents.Mobile, _mobile)
                .AddColumn(Tables.Agents.MSN, _msn)
                .AddColumn(Tables.Agents.Name, _name)
                .AddColumn(Tables.Agents.PostCode, _postCode)
                .AddColumn(Tables.Agents.Tel, _tel);
            _id = insert.ExecuteReturnAutoIncrementID(Tables.Agents.Id);
        }

        [Transactional(TransactionalTypes.TransactionScope)]
        protected override void DataPortal_Update()
        {
            DataService.DBService.Update(Tables.Agents)
                .AddColumn(Tables.Agents.Address, this._address)
                .AddColumn(Tables.Agents.DealTimes, this._dealTimes)
                .AddColumn(Tables.Agents.Email, _email)
                .AddColumn(Tables.Agents.Fax, _fax)
                .AddColumn(Tables.Agents.Mobile, _mobile)
                .AddColumn(Tables.Agents.MSN, _msn)
                .AddColumn(Tables.Agents.Name, _name)
                .AddColumn(Tables.Agents.PostCode, _postCode)
                .AddColumn(Tables.Agents.Tel, _tel)
                .Where(Tables.Agents.Id == _id).Execute();
        }

        protected override void DataPortal_DeleteSelf()
        {
            DataPortal_Delete(new Criteria(_id));
        }

        [Transactional(TransactionalTypes.TransactionScope)]
        private void DataPortal_Delete(Criteria criteria)
        {
            DataService.DBService.Delete(Tables.Agents).Where(Tables.Agents.Id == criteria.Id).Execute();
        }


        #endregion

        public static bool CheckNameIsUsed(string name)
        {
            CheckNameCommand cmd = new CheckNameCommand(name);
            Csla.DataPortal.Execute<CheckNameCommand>(cmd);
            return cmd.Result;
        }

        #region CheckNameCommand

        [Serializable()]
        private class CheckNameCommand : CommandBase
        {
            #region Client-side Code

            string _name;
            bool _result = false;
            /// <summary>
            /// Gets a value indicating whether this <see cref="CheckNameCommand"/> is result.
            /// </summary>
            /// <value><c>true</c> if result; otherwise, <c>false</c>.</value>
            public bool Result
            {
                get { return _result; }
            }

            #endregion

            #region Factory Methods

            public CheckNameCommand(string name)
            {
                _name = name;
            }

            #endregion

            #region Server-side Code

            [Transactional(TransactionalTypes.TransactionScope)]
            protected override void DataPortal_Execute()
            {
                int c = (int)DB.DataService.DBService.Select(Tables.Agents, Tables.Agents.Id.Count())
                    .Where(Tables.Agents.Name == _name).ToScalar();
                _result = c != 0;
            }
            #endregion
        }

        #endregion
    }
}
